package orxatas.travelme.sync;

import java.util.ArrayList;
import java.util.List;

import org.apache.http.NameValuePair;
import org.apache.http.message.BasicNameValuePair;

import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;

import orxatas.travelme.activity.AsyncActivity;
import orxatas.travelme.databases.TravelmeDatabase;
import orxatas.travelme.databases.exceptions.UserNotInLocal;
import orxatas.travelme.entity.User;
import orxatas.travelme.manager.AccountManager;
import orxatas.travelme.manager.DataManager;
import orxatas.travelme.manager.FriendManager;
import orxatas.travelme.manager.UserManager;
import orxatas.travelme.sync.SyncData.SyncDataType;
import orxatas.travelme.sync.exceptions.ImposibleAddNewFriend;
import orxatas.travelme.sync.exceptions.ImposibleDelFriend;

/**
 * Módulo de sincronización de amigos. Será quien maneje los datos locales sincornizados.
 * Convenio: Sólo la deben de usar los respectivos managers.
 */
public class SyncFriends extends SyncOptions {
	
	/**
	 * True si hay una petición de actualización en curso. En caso contrario false.
	 * */
	static private boolean synchronizing = false;
	
	/**
	 * Listado de amigos.
	 * */
	static private ArrayList<Integer> friendList = null;
	
	/**
	 * Instancia del manejador de amigos.
	 * */
	private FriendManager friendManager;
	
	/**
	 * Contexto de la aplicación. Actividad.
	 * */
	private AsyncActivity asyncActivity;
	
	/**
	 * Base de datos.
	 * */
	private SQLiteDatabase trvlmdb;
	private TravelmeDatabase databaseHelper;

	private SyncDataType syncData;
	
	/**
	 * Sincronizador de amigos.
	 * 
	 * @param friendManager Manejador de amigos.
	 * @param asyncActivity Actividad ejecutora.
	 * */
	public SyncFriends(FriendManager friendManager, AsyncActivity asyncActivity){
		this.friendManager = friendManager;
		this.asyncActivity = asyncActivity;
		
		databaseHelper = new TravelmeDatabase(asyncActivity.getActivity(), new AccountManager(asyncActivity).getUserLogged().getId());
		
		//if(checkIfSyncIsNeeded()) synchronize(null);
	}
	
	/**
	 * Comprueba si actualmente existe alguna petición de sincronización en curso.
	 * 
	 * @return  Devuelve true si hay una petición de actualización en curso. En caso contrario devuelve false.
	 * */
	public boolean isSynchronizing(){
		return synchronizing;
	}
	
	/**
	 * Recupera toda la lista de amigos almacenada localmente.
	 * */
	private void loadFromDB(){
		trvlmdb = databaseHelper.getReadableDatabase();
		Cursor c = trvlmdb.rawQuery("SELECT * FROM "+TravelmeDatabase.FRIEND_TNAME, null);
		
		int idc_id = c.getColumnIndex(TravelmeDatabase.FRIEND_CID);
		
		friendList = new ArrayList<Integer>();
		while(c.moveToNext()){
			int userID = c.getInt(idc_id);
			friendList.add(userID);
		}
		c.close();
		trvlmdb.close();
	}
	
	/**
	 * Comprueba si es necesario sincronizar datos.
	 * 
	 * @return True si se considera que los datos están desactualizados.
	 * */
	private boolean checkIfSyncIsNeeded(){
		//TODO
		return true;
	}
	
	private class AnswerFriendList extends Answer<ArrayList<User>>{};
	/**
	 * An Asynchronous call to webservice for sync friend list
	 * */
	private class FriendSyncCall extends AsyncInternetConnection{
		public FriendSyncCall() {
			super(TRVLMWS);
			
			List<NameValuePair> pairs = basicGETParams(new AccountManager(asyncActivity), METHOD_FM, ACTION_FM_LIST);
			addGETParams(pairs);
		}

		@Override
		protected void onPostExecute(String response) {
			boolean usersChanged = false;
			boolean friendListChanged = false;
			try {
				Answer<ArrayList<User>> answer = new ParseAnswer<AnswerFriendList>().parse(response, true, AnswerFriendList.class);
				if(answer.getState() == 0){
					ArrayList<User> newFriendList = answer.getObj();
					
					trvlmdb = databaseHelper.getWritableDatabase();
					for(User u: newFriendList){
						boolean was = false;
						for(int oldu: friendList){
							if(u.getId() == oldu)
								was = true;
						}
						//User oldU = friendManager.getFriend(newU.getId());
						
						Cursor c = trvlmdb.rawQuery("SELECT * FROM "+TravelmeDatabase.USER_TNAME+" WHERE "+TravelmeDatabase.USER_CID+" = "+u.getId(), null);
						if(c.moveToFirst()){
							ContentValues vu = new ContentValues();
							vu.put(TravelmeDatabase.USER_CLASTUPDATE, u.getLastUpdate());
							vu.put(TravelmeDatabase.USER_CEMAIL, u.getEmail());
							vu.put(TravelmeDatabase.USER_CNAME, u.getUserName());
							
							trvlmdb.update(TravelmeDatabase.USER_TNAME, vu, TravelmeDatabase.USER_CID+" = "+u.getId(), null);
							usersChanged = true;
						} else {
							ContentValues vu = new ContentValues();
							vu.put(TravelmeDatabase.USER_CLASTUPDATE, u.getLastUpdate());
							vu.put(TravelmeDatabase.USER_CEMAIL, u.getEmail());
							vu.put(TravelmeDatabase.USER_CNAME, u.getUserName());
							vu.put(TravelmeDatabase.USER_CID, u.getId());
							
							trvlmdb.insert(TravelmeDatabase.USER_TNAME, null, vu);
							
							usersChanged = true;
						}
						c.close();
						
						if(!was){
							ContentValues vf = new ContentValues();
							vf.put(TravelmeDatabase.FRIEND_CID, u.getId());
							trvlmdb.insert(TravelmeDatabase.FRIEND_TNAME, null, vf);
							friendListChanged = true;
						}
					}
					
					for(int oldf: friendList){
						boolean was = false;
						for(User u: newFriendList ){
							if(oldf == u.getId())
								was = true;
						}
						
						if(!was){
							trvlmdb.delete(TravelmeDatabase.USER_TNAME, TravelmeDatabase.USER_CID+" = "+oldf, null);
							trvlmdb.delete(TravelmeDatabase.FRIEND_TNAME, TravelmeDatabase.FRIEND_CID+" = "+oldf, null);
							friendListChanged = true;
						}
					}
					trvlmdb.close();
				}
			} catch (WithoutObject e) {
				Log.e("SYNC", "Bad sync with friend list.");
			}
			
			loadFromDB();
			
			synchronizing = false;
			DataManager dm = new DataManager(asyncActivity);
			
			//Next to sync
			if(syncData != null){
				dm.syncThis(syncData);
				syncData = null;
			} else
				dm.syncInProgressEnd();
			
			if(usersChanged)
				dm.asyncNotice(AsyncNoticeCode.USERS_CHANGED);
			if(friendListChanged)
				dm.asyncNotice(AsyncNoticeCode.FRIENDS_CHANGED);
		}
	}
	
	/**
	 * Fuerza la ejecuón de una sincronización siempre y cuando no haya una petición en curso.
	 * */
	public void synchronize(SyncDataType syncData){
		synchronizing = true;
		
		new DataManager(asyncActivity).syncInProgress();
		this.syncData = syncData;
		
		new FriendSyncCall().execute();
	}
	
	/**
	 * Petición para obtener los amigos. Devuelve inmediatamente los amigos locales. Los datos pueden estar desactualizados.
	 * 
	 * @return Lista de amigos.
	 * */
	public ArrayList<Integer> getFriendList(){
		
		trvlmdb = databaseHelper.getReadableDatabase();
		Cursor c = trvlmdb.rawQuery("SELECT * FROM "+TravelmeDatabase.FRIEND_TNAME, null);
		
		int idc_id = c.getColumnIndex(TravelmeDatabase.FRIEND_CID);
		
		friendList = new ArrayList<Integer>();
		while(c.moveToNext()){
			int userID = c.getInt(idc_id);
			friendList.add(userID);
		}
		c.close();
		trvlmdb.close();
		
		return friendList;
	}
	
	private class AnswerAddNewFriend extends Answer<User>{};
	/**
	 * Añade un nuevo amigo. Llamada síncrona.
	 * 
	 * @param friendEmail Correo electrónico del amigo.
	 * 
	 * @throws ImposibleAddNewFriend Si ha habido algún error a la hora de añadir el nuevo amigo.
	 * */
	public User addNewFriend(String friendMail) throws ImposibleAddNewFriend{
		SyncInternetConnection newFriendCall = new SyncInternetConnection(TRVLMWS);
		
		List<NameValuePair> pairs = basicGETParams(new AccountManager(asyncActivity), METHOD_FM, ACTION_FM_ADDF );
		pairs.add(new BasicNameValuePair("friend", friendMail));
		newFriendCall.addGETParams(pairs);
		
		String response = newFriendCall.send();
		
		try {
			Answer<User> answer = new ParseAnswer<AnswerAddNewFriend>().parse(response, true, AnswerAddNewFriend.class);
			User newFriend;
			if(answer.getState() == 0)
				newFriend = answer.getObj();
			else
				throw new ImposibleAddNewFriend(friendMail);
			
			trvlmdb = databaseHelper.getWritableDatabase();
			
			//Add new friend
			ContentValues v = new ContentValues();
			v.put(TravelmeDatabase.FRIEND_CID, newFriend.getId());
			trvlmdb.insert(TravelmeDatabase.FRIEND_TNAME, null, v);
			
			//Save user information.
			v = new ContentValues();
			v.put(TravelmeDatabase.USER_CID, newFriend.getId());
			v.put(TravelmeDatabase.USER_CEMAIL, newFriend.getEmail());
			v.put(TravelmeDatabase.USER_CNAME, newFriend.getUserName());
			v.put(TravelmeDatabase.USER_CLASTUPDATE, newFriend.getLastUpdate());
			trvlmdb.insert(TravelmeDatabase.USER_TNAME, null, v);
			
			trvlmdb.close();
			
			return newFriend;
		} catch (WithoutObject e) {
			throw new ImposibleAddNewFriend(friendMail);
		}
	}
	
	/**
	 * Elimina a un amigo. Llamada SÍNCRONA.
	 * 
	 * @param idFriend Identificador del amigo a eliminar.
	 * @throws ImposibleDelFriend 
	 * */
	public void removeFriend(int idFriend) throws ImposibleDelFriend{
		SyncInternetConnection delFriendCall = new SyncInternetConnection(TRVLMWS);
		
		List<NameValuePair> pairs = basicGETParams(new AccountManager(asyncActivity), METHOD_FM, ACTION_FM_DELF );
		pairs.add(new BasicNameValuePair("fid", ""+idFriend));
		delFriendCall.addGETParams(pairs);
		String response = delFriendCall.send();

		Answer answer = new ParseAnswer<Answer>().parse(response, Answer.class);
		if(answer.getState() == 0){
			trvlmdb = databaseHelper.getWritableDatabase();
			trvlmdb.delete(TravelmeDatabase.FRIEND_TNAME, TravelmeDatabase.FRIEND_CID+" = "+idFriend, null);
			trvlmdb.delete(TravelmeDatabase.USER_TNAME, TravelmeDatabase.USER_CID+" = "+idFriend, null);
			trvlmdb.close();
		} else {
			throw new ImposibleDelFriend(idFriend);
		}
	}
}
